package task

import (
	"context"
	"fmt"
	"im/models"
	"sync"
	"time"
)

// TaskQueue 管理任务和时间轮
type TaskQueue struct {
	queue       chan *models.FailTask            //队列存储失败任务
	timeWheel   map[time.Time][]*models.FailTask //时间轮
	timerMutex  sync.Mutex                       //锁
	currentTime time.Time                        //当前时间
	ticker      *time.Ticker                     //定时器
	done        chan bool                        //
}

var TaskQ *TaskQueue

func FailTaskInit() {
	NewTaskQueue()
}

// NewTaskQueue 创建一个新的任务队列
func NewTaskQueue() {
	TaskQ = &TaskQueue{
		queue:       make(chan *models.FailTask),
		timeWheel:   make(map[time.Time][]*models.FailTask),
		currentTime: time.Now(),
		ticker:      time.NewTicker(1 * time.Second), // 默认每秒检查一次时间轮
		done:        make(chan bool),
	}
	TaskQ.Start()
}

// AddTask 将任务添加到队列中，并设置初始重试时间
func (tq *TaskQueue) AddTask(task *models.FailTask) {
	task.RetryAt = tq.currentTime.Add(1 * time.Second) // 初始重试时间设置为当前时间加1秒
	task.Delay = 1 * time.Second                       // 初始延迟时间
	tq.queue <- task
}

// Start 处理任务队列和时间轮
func (tq *TaskQueue) Start() {
	go func() {
		for {
			select {
			case task := <-tq.queue:
				tq.addToTimeWheel(task)
			case <-tq.ticker.C:
				tq.tick()
			case <-tq.done:
				tq.ticker.Stop()
				return
			}
		}
	}()
}

// addToTimeWheel 将任务添加到时间轮中
func (tq *TaskQueue) addToTimeWheel(task *models.FailTask) {
	tq.timerMutex.Lock()
	defer tq.timerMutex.Unlock()
	tq.timeWheel[task.RetryAt] = append(tq.timeWheel[task.RetryAt], task)
}

// tick 每隔一段时间检查时间轮并执行到期的任务
func (tq *TaskQueue) tick() {
	tq.timerMutex.Lock()
	defer tq.timerMutex.Unlock()

	now := time.Now()
	if now.After(tq.currentTime) {
		tq.currentTime = now.Truncate(time.Second) // 修正当前时间到秒级别

		// 遍历时间轮，找到并移除到期的任务
		for key, tasks := range tq.timeWheel {
			if key.Before(tq.currentTime) || key.Equal(tq.currentTime) {
				for _, task := range tasks {
					go tq.executeTask(task)
				}
				delete(tq.timeWheel, key)
			}
		}
	}
}

// executeTask 执行任务，如果失败则重新加入时间轮
func (tq *TaskQueue) executeTask(task *models.FailTask) {
	// 模拟任务执行，这里总是假设失败
	fmt.Printf("执行失败任务:", task)
	f := handlers[task.ActionType]
	if f(context.Background(), task) != nil {
		// 假设执行失败，更新重试时间和延迟
		task.RetryAt = time.Now().Add(task.Delay * 2)
		task.Delay *= 2
		// 将任务重新加入队列（实际上应直接修改时间轮）
		tq.addToTimeWheel(task)
	}
}

// Stop 停止任务队列
func (tq *TaskQueue) Stop() {
	tq.done <- true
}
